//
// Created by Soar Qin on 2017/8/18.
// Copyright (c) 2017 Soar Qin. All rights reserved.
//

#include "common.hh"

#include "io_event.hh"

#include "loop.hh"

#include "core/async/private/io_event_private.hh"
#include "private/loop_private.hh"

namespace core::async {

static void ioEventCB(evutil_socket_t fd, short events, void *arg) {
    auto *reqev = static_cast<IoEvent *>(arg);
    reqev->onEvent(events);
}

IoEvent::IoEvent(Loop &loop): IoBase(loop), context_(new IoEventContext) {
}

IoEvent::~IoEvent() {
    if (context_->ev != nullptr) {
        event_free(context_->ev);
    }
    delete context_;
}

bool IoEvent::close() {
    bool result = false;
    if (context_->ev != nullptr) {
        result = true;
        event_free(context_->ev);
        context_->ev = nullptr;
    }
    if (context_->fd >= 0) {
        evutil_closesocket(context_->fd);
        context_->fd = -1;
    }
    context_->enabled = 0;
    return result;
}

void IoEvent::enableInternal(short op) {
    if (op != context_->enabled) {
        event_del(context_->ev);
        event_assign(context_->ev, loop_.getContext()->evbase, context_->fd, op, ioEventCB, this);
        event_add(context_->ev, nullptr);
    }
    context_->enabled = op;
}

bool IoEvent::startInternal(int fd, short op) {
    evutil_socket_t fd_ = fd;
    context_->fd = fd_;
    context_->enabled = op;
    auto *context = loop_.getContext();
    context_->ev = event_new(context->evbase, fd_, op, ioEventCB, this);
    if (context_->ev == nullptr) {
        context_->fd = -1; context_->enabled = 0;
        return false;
    }
    if ((op & (EV_READ | EV_WRITE | EV_SIGNAL)) != 0) {
        if (event_add(context_->ev, nullptr) < 0) {
            event_free(context_->ev);
            context_->ev = nullptr;
            context_->fd = -1;
            context_->enabled = 0;
            return false;
        }
    }
    return true;
}

}
